using System.Collections;
using System.Text;
using ufolder;

using System;

namespace ufolder {



public class Parser {
	const int _EOF = 0;
	const int _word = 1;
	const int maxT = 21;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

public string SQL="";
private SQLGenerator sqlGenerator = new SQLGenerator();

private bool checkValidDate(string d)
{
	bool s = d.Length == 10;
	//TODO
	return s;
}

/*public void ProcessToken(SqlGenProc p)
{
	sqlGenerator.AddProc(p);
	sqlGenerator.AddParam(new string[] {this.t.val.ToString()});
	sqlGenerator.ProcessQueue();
}*/

/*------------------------------------------------------------------------*
 *----- SCANNER DESCRIPTION ----------------------------------------------*
 *------------------------------------------------------------------------*/


	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void UFolderQuery() {
		sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.Intersect)); 
		Groupe();
		while (StartOf(1)) {
			Groupe();
		}
		sqlGenerator.ProcessQueue(); 
		SQL = sqlGenerator.SQL; 
		
	}

	void Groupe() {
		if (StartOf(2)) {
			Predicate();
			sqlGenerator.ParamPropag(); 
		} else if (la.kind == 2) {
			GroupeAnd();
			sqlGenerator.ParamPropag(); 
		} else if (la.kind == 4) {
			GroupeOr();
			sqlGenerator.ParamPropag(); 
		} else SynErr(22);
	}

	void Predicate() {
		bool predicNeg = false; 
		if (la.kind == 6) {
			Get();
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.Complemente)); predicNeg = true; 
		}
		if (la.kind == 1) {
			Get();
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.ContainText)); sqlGenerator.AddParam(new string[] {this.t.val.ToString()}); 
		} else if (la.kind == 11 || la.kind == 12) {
			PredicateLabel();
		} else if (la.kind == 7 || la.kind == 8 || la.kind == 9) {
			PredicateWord();
		} else if (la.kind == 13 || la.kind == 14 || la.kind == 15) {
			PredicateDate();
		} else if (la.kind == 16) {
			PredicateHas();
		} else SynErr(23);
		sqlGenerator.ProcessQueue(); if(predicNeg) { sqlGenerator.ParamPropag(); sqlGenerator.ProcessQueue(); } 
	}

	void GroupeAnd() {
		sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.Intersect)); 
		Expect(2);
		Groupe();
		while (StartOf(1)) {
			Groupe();
		}
		Expect(3);
		sqlGenerator.ProcessQueue(); 
	}

	void GroupeOr() {
		sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.Union)); 
		Expect(4);
		Groupe();
		while (StartOf(1)) {
			Groupe();
		}
		Expect(5);
		sqlGenerator.ProcessQueue(); 
	}

	void PredicateLabel() {
		if (la.kind == 11) {
			Get();
		} else if (la.kind == 12) {
			Get();
		} else SynErr(24);
		ValueLabel();
	}

	void PredicateWord() {
		int target = 0; 
		if (la.kind == 7) {
			Get();
			target = 0;  
		} else if (la.kind == 8) {
			Get();
			target = 1;  
		} else if (la.kind == 9) {
			Get();
			target = 2;  
		} else SynErr(25);
		Expect(10);
		ValueWord(target);
	}

	void PredicateDate() {
		if (la.kind == 13 || la.kind == 14) {
			PredicateDateStrict();
		} else if (la.kind == 15) {
			PredicateDateLarge();
		} else SynErr(26);
	}

	void PredicateHas() {
		Expect(16);
		ValueHas();
	}

	void ValueWord(int target) {
		ArrayList AWord = new ArrayList();
		ArrayList ANWord = new ArrayList();
		
		if (la.kind == 1 || la.kind == 6) {
			Texte(AWord, ANWord);
			switch(target)
			{
				case 0: // Cible le titre
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TitreContains));
					break;
				case 1: //Cible texte
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TexteContains));
					break;
					
				case 2: //Cible le type
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TypeContains));
					break;
			}
			sqlGenerator.AddParam((string[])AWord.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANWord.ToArray(typeof(string)));
			
		} else if (la.kind == 2) {
			AndValWord(AWord, ANWord);
			switch(target)
			{
				case 0: // Cible le titre
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TitreContains));
					break;
				case 1: //Cible texte
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TexteContains));
					break;
					
				case 2: //Cible le type
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TypeContains));
					break;
			}
			sqlGenerator.AddParam((string[])AWord.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANWord.ToArray(typeof(string)));
			
		} else if (la.kind == 4) {
			OrValWord(AWord, ANWord);
			switch(target)
			{
				case 0: // Cible le titre
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TitreContainsOr));
					break;
				case 1: //Cible texte
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TexteContainsOr));
					break;
					
				case 2: //Cible le type
					sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.TypeContainsOr));
					break;
			}
			sqlGenerator.AddParam((string[])AWord.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANWord.ToArray(typeof(string)));
			
		} else SynErr(27);
	}

	void ValueLabel() {
		ArrayList ALabel = new ArrayList();
		ArrayList ALGroup = new ArrayList();
		ArrayList ANLabel = new ArrayList();
		ArrayList ANLGroup = new ArrayList();
		
		if (la.kind == 1 || la.kind == 6 || la.kind == 20) {
			Label(ALabel, ALGroup, ANLabel, ANLGroup);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.ContainLabels));
			sqlGenerator.AddParam((string[])ALabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ALGroup.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLGroup.ToArray(typeof(string)));
			
			
		} else if (la.kind == 2) {
			AndValLabel(ALabel, ALGroup, ANLabel, ANLGroup);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.ContainLabels));
			sqlGenerator.AddParam((string[])ALabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ALGroup.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLGroup.ToArray(typeof(string)));
			
			
		} else if (la.kind == 4) {
			OrValLabel(ALabel, ALGroup, ANLabel, ANLGroup);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.ContainAnyLabels));
			sqlGenerator.AddParam((string[])ALabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ALGroup.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLabel.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])ANLGroup.ToArray(typeof(string)));
			
		} else SynErr(28);
	}

	void PredicateDateStrict() {
		if (la.kind == 13) {
			Get();
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.BeforeDate)); 
		} else if (la.kind == 14) {
			Get();
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.AfterDate)); 
		} else SynErr(29);
		Expect(10);
		Expect(1);
		if(!checkValidDate(this.t.val.ToString())) this.errors.SemErr("Date invalide");
		sqlGenerator.AddParam(new string[] {this.t.val.ToString()}); 
	}

	void PredicateDateLarge() {
		Expect(15);
		sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.SavedAt)); 
		ValueDate();
	}

	void ValueDate() {
		if (la.kind == 1) {
			Get();
			if(!checkValidDate(this.t.val.ToString())) this.errors.SemErr("Date invalide");
			sqlGenerator.AddParam(new string[] {this.t.val.ToString()});	
		} else if (la.kind == 4) {
			OrValDate();
		} else SynErr(30);
	}

	void ValueHas() {
		ArrayList AHas = new ArrayList();
		ArrayList AHasNot = new ArrayList();
		
		if (StartOf(3)) {
			Has(AHas, AHasNot);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.HasAll));
			sqlGenerator.AddParam((string[])AHas.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])AHasNot.ToArray(typeof(string)));
			
		} else if (la.kind == 2) {
			AndValHas(AHas, AHasNot);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.HasAll));
			sqlGenerator.AddParam((string[])AHas.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])AHasNot.ToArray(typeof(string)));
			
		} else if (la.kind == 4) {
			OrValHas(AHas, AHasNot);
			sqlGenerator.AddProc(new SqlGenProc(this.sqlGenerator.HasAny));
			sqlGenerator.AddParam((string[])AHas.ToArray(typeof(string)));
			sqlGenerator.AddParam((string[])AHasNot.ToArray(typeof(string)));
			
		} else SynErr(31);
	}

	void Has(ArrayList AHas, ArrayList AHasNot) {
		bool neg = false; 
		if (la.kind == 6) {
			Get();
			neg = true; 
		}
		if (la.kind == 17) {
			Get();
			if(neg) AHas.Add("uri"); else AHasNot.Add("uri"); 
		} else if (la.kind == 8) {
			Get();
			if(neg) AHas.Add("texte"); else AHasNot.Add("texte"); 
		} else if (la.kind == 18) {
			Get();
			if(neg) AHas.Add("tag"); else AHasNot.Add("tag"); 
		} else if (la.kind == 19) {
			Get();
			if(neg) AHas.Add("tag"); else AHasNot.Add("tag"); 
		} else SynErr(32);
	}

	void AndValHas(ArrayList AHas, ArrayList AHasNot) {
		Expect(2);
		Has(AHas, AHasNot);
		while (StartOf(3)) {
			Has(AHas, AHasNot);
		}
		Expect(3);
	}

	void OrValHas(ArrayList AHas, ArrayList AHasNot) {
		Expect(4);
		Has(AHas, AHasNot);
		while (StartOf(3)) {
			Has(AHas, AHasNot);
		}
		Expect(5);
	}

	void Label(ArrayList Label, ArrayList LGroup, ArrayList NLabel, ArrayList NLGroup) {
		bool group = false; bool neg = false; 
		if (la.kind == 6) {
			Get();
			neg = true; 
		}
		if (la.kind == 20) {
			Get();
			group = true; 
		}
		Expect(1);
		if (neg)
		{
			if (group) NLGroup.Add(this.t.val.ToLower());
			else NLabel.Add(this.t.val.ToLower());
		}
		else
		{
			if (group) LGroup.Add(this.t.val.ToLower());
			else Label.Add(this.t.val.ToLower());
		}
		
	}

	void AndValLabel(ArrayList ALabel, ArrayList ALGroup, ArrayList ANLabel, ArrayList ANLGroup) {
		Expect(2);
		Label(ALabel, ALGroup, ANLabel, ANLGroup);
		while (la.kind == 1 || la.kind == 6 || la.kind == 20) {
			Label(ALabel, ALGroup, ANLabel, ANLGroup);
		}
		Expect(3);
	}

	void OrValLabel(ArrayList ALabel, ArrayList ALGroup, ArrayList ANLabel, ArrayList ANLGroup) {
		Expect(4);
		Label(ALabel, ALGroup, ANLabel, ANLGroup);
		while (la.kind == 1 || la.kind == 6 || la.kind == 20) {
			Label(ALabel, ALGroup, ANLabel, ANLGroup);
		}
		Expect(5);
	}

	void Texte(ArrayList AWord, ArrayList ANWord) {
		bool neg = false; 
		if (la.kind == 6) {
			Get();
			neg = true; 
		}
		Expect(1);
		if (neg) ANWord.Add(this.t.val.ToString()); else AWord.Add(this.t.val.ToString()); 
	}

	void AndValWord(ArrayList AWord, ArrayList ANWord) {
		Expect(2);
		Texte(AWord, ANWord);
		while (la.kind == 1 || la.kind == 6) {
			Texte(AWord, ANWord);
		}
		Expect(3);
	}

	void OrValWord(ArrayList AWord, ArrayList ANWord) {
		Expect(4);
		Texte(AWord, ANWord);
		while (la.kind == 1 || la.kind == 6) {
			Texte(AWord, ANWord);
		}
		Expect(5);
	}

	void OrValDate() {
		Expect(4);
		Expect(1);
		if(!checkValidDate(this.t.val.ToString())) this.errors.SemErr("Date invalide");
		sqlGenerator.AddParam(new string[] {this.t.val.ToString()}); 
		while (la.kind == 1) {
			Get();
			if(!checkValidDate(this.t.val.ToString())) this.errors.SemErr("Date invalide");
			sqlGenerator.AddParam(new string[] {this.t.val.ToString()}); 
		}
		Expect(5);
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		UFolderQuery();

    Expect(0);
	}
	
	static readonly bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
		{x,T,T,x, T,x,T,T, T,T,x,T, T,T,T,T, T,x,x,x, x,x,x},
		{x,T,x,x, x,x,T,T, T,T,x,T, T,T,T,T, T,x,x,x, x,x,x},
		{x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,T,T,T, x,x,x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	//public System.IO.TextWriter errorStream = Console.Out;   // error messages go to this stream
	public System.IO.StringWriter errorStream = new System.IO.StringWriter(new System.Text.StringBuilder());
  public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text
  
	public void SynErr (int line, int col, int n) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "word expected"; break;
			case 2: s = "\"(\" expected"; break;
			case 3: s = "\")\" expected"; break;
			case 4: s = "\"{\" expected"; break;
			case 5: s = "\"}\" expected"; break;
			case 6: s = "\"-\" expected"; break;
			case 7: s = "\"titre\" expected"; break;
			case 8: s = "\"texte\" expected"; break;
			case 9: s = "\"type\" expected"; break;
			case 10: s = "\":\" expected"; break;
			case 11: s = "\"tag:\" expected"; break;
			case 12: s = "\"label:\" expected"; break;
			case 13: s = "\"before\" expected"; break;
			case 14: s = "\"after\" expected"; break;
			case 15: s = "\"at:\" expected"; break;
			case 16: s = "\"has:\" expected"; break;
			case 17: s = "\"uri\" expected"; break;
			case 18: s = "\"tag\" expected"; break;
			case 19: s = "\"label\" expected"; break;
			case 20: s = "\"<\" expected"; break;
			case 21: s = "??? expected"; break;
			case 22: s = "invalid Groupe"; break;
			case 23: s = "invalid Predicate"; break;
			case 24: s = "invalid PredicateLabel"; break;
			case 25: s = "invalid PredicateWord"; break;
			case 26: s = "invalid PredicateDate"; break;
			case 27: s = "invalid ValueWord"; break;
			case 28: s = "invalid ValueLabel"; break;
			case 29: s = "invalid PredicateDateStrict"; break;
			case 30: s = "invalid ValueDate"; break;
			case 31: s = "invalid ValueHas"; break;
			case 32: s = "invalid Has"; break;

			default: s = "error " + n; break;
		}
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}

	public void SemErr (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}
	
	public void SemErr (string s) {
		errorStream.WriteLine(s);
		count++;
	}
	
	public void Warning (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
	}
	
	public void Warning(string s) {
		errorStream.WriteLine(s);
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}

}